]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
keyboard: Implement standalone FocusTracker
authorCarlos Garnacho <carlosg@gnome.org>
Fri, 25 May 2018 09:35:49 +0000 (11:35 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Wed, 11 Jul 2018 16:32:32 +0000 (18:32 +0200)
And stop using FocusCaretTracker for caret position purposes. This
new object uses 1) the text-input protocol in wayland and 2) Info
from IBusPanelService for X11 (which is meant to work for XIM too).

This drops the usage of AtspiEventListener for OSK purposes, which
is best to avoid.

js/misc/ibusManager.js
js/ui/keyboard.js

index 782b9ad28e420b6a7b03445358e5bdc1e8e50b51..6452e492ffe46037500ed9a7c7da7cc2566aa230 100644 (file)
@@ -115,6 +115,11 @@ var IBusManager = new Lang.Class({
                                                          object_path: IBus.PATH_PANEL });
             this._candidatePopup.setPanelService(this._panelService);
             this._panelService.connect('update-property', this._updateProperty.bind(this));
+            this._panelService.connect('set-cursor-location', (ps, x, y, w, h) => {
+                let cursorLocation = { x, y, width: w, height: h };
+                this.emit('set-cursor-location', cursorLocation);
+            });
+
             try {
                 // IBus versions older than 1.5.10 have a bug which
                 // causes spurious set-content-type emissions when
index 71b8f22dc3a8c14fd90b1cbe6efbe901e45a7857..9003720723038820f428d11c7b0610fb44cee622 100644 (file)
@@ -1,6 +1,5 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 
-const FocusCaretTracker = imports.ui.focusCaretTracker;
 const Atspi = imports.gi.Atspi;
 const Clutter = imports.gi.Clutter;
 const Gdk = imports.gi.Gdk;
@@ -13,6 +12,7 @@ const Signals = imports.signals;
 const St = imports.gi.St;
 const InputSourceManager = imports.ui.status.keyboard;
 
+const IBusManager = imports.misc.ibusManager;
 const BoxPointer = imports.ui.boxpointer;
 const Layout = imports.ui.layout;
 const Main = imports.ui.main;
@@ -261,6 +261,7 @@ var Key = new Lang.Class({
         this._extended_keyboard = null;
         this._pressTimeoutId = 0;
         this._capturedPress = false;
+
         this._capturedEventId = 0;
         this._unmapId = 0;
         this._longPress = false;
@@ -484,6 +485,71 @@ var KeyboardModel = new Lang.Class({
     }
 });
 
+var FocusTracker = new Lang.Class({
+    Name: 'FocusTracker',
+
+    _init() {
+        this._currentWindow = null;
+        this._currentWindowPositionId = 0;
+
+        global.screen.get_display().connect('notify::focus-window', () => {
+            this._setCurrentWindow(global.screen.get_display().focus_window);
+            this.emit('window-changed', this._currentWindow);
+        });
+
+        /* Valid for wayland clients */
+        Main.inputMethod.connect('cursor-location-changed', (o, rect) => {
+            let newRect = { x: rect.get_x(), y: rect.get_y(), width: rect.get_width(), height: rect.get_height() };
+            this._setCurrentRect(newRect);
+        });
+
+        this._ibusManager = IBusManager.getIBusManager();
+        this._ibusManager.connect('set-cursor-location', (manager, rect) => {
+            /* Valid for X11 clients only */
+            if (Main.inputMethod.currentFocus)
+                return;
+
+            this._setCurrentRect(rect);
+        });
+    },
+
+    get currentWindow() {
+        return this._currentWindow;
+    },
+
+    _setCurrentWindow(window) {
+        if (this._currentWindow)
+            this._currentWindow.disconnect(this._currentWindowPositionId);
+
+        this._currentWindow = window;
+        if (window) {
+            this._currentWindowPositionId = this._currentWindow.connect('position-changed', () => {
+                if (global.display.get_grab_op() == Meta.GrabOp.NONE)
+                    this.emit('position-changed');
+                else
+                    this.emit('reset');
+            });
+        }
+    },
+
+    _setCurrentRect(rect) {
+        let frameRect = this._currentWindow.get_frame_rect();
+        rect.x -= frameRect.x;
+        rect.y -= frameRect.y;
+
+        this._rect = rect;
+        this.emit('position-changed');
+    },
+
+    getCurrentRect() {
+        let frameRect = this._currentWindow.get_frame_rect();
+        let rect = { x: this._rect.x + frameRect.x, y: this._rect.y + frameRect.y, width: this._rect.width, height: this._rect.height };
+
+        return rect;
+    }
+});
+Signals.addSignalMethods(FocusTracker.prototype);
+
 var Keyboard = new Lang.Class({
     Name: 'Keyboard',
 
@@ -491,15 +557,10 @@ var Keyboard = new Lang.Class({
         this.actor = null;
         this._focusInExtendedKeys = false;
 
-        this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker();
-        this._focusCaretTracker.connect('focus-changed', this._onFocusChanged.bind(this));
-        this._focusCaretTracker.connect('caret-moved', this._onCaretMoved.bind(this));
         this._languagePopup = null;
-        this._currentAccessible = null;
-        this._caretTrackingEnabled = false;
-        this._updateCaretPositionId = 0;
         this._currentFocusWindow = null;
-        this._originalWindowY = null;
+        this._animFocusedWindow = null;
+        this._delayedAnimFocusWindow = null;
 
         this._enableKeyboard = false; // a11y settings value
         this._enabled = false; // enabled state (by setting or device type)
@@ -510,6 +571,14 @@ var Keyboard = new Lang.Class({
         this._lastDeviceId = null;
         this._suggestions = null;
 
+        this._focusTracker = new FocusTracker();
+        this._focusTracker.connect('position-changed', this._onFocusPositionChanged.bind(this));
+        this._focusTracker.connect('reset', () => {
+            this._delayedAnimFocusWindow = null;
+            this._animFocusedWindow = null;
+            this._oskFocusWindow = null;
+        });
+
         Meta.get_backend().connect('last-device-changed', 
             (backend, deviceId) => {
                 let manager = Clutter.DeviceManager.get_default();
@@ -532,102 +601,15 @@ var Keyboard = new Lang.Class({
         this._keyboardRestingId = 0;
 
         Main.layoutManager.connect('monitors-changed', this._relayout.bind(this));
-        //Main.inputMethod.connect('cursor-location-changed', (o, rect) => {
-        //    if (this._keyboardVisible) {
-        //        let currentWindow = global.display.focus_window;
-        //        this.setCursorLocation(currentWindow, rect.get_x(), rect.get_y(),
-        //                               rect.get_width(), rect.get_height());
-        //    }
-        //});
     },
 
     get visible() {
         return this._keyboardVisible;
     },
 
-    _setCaretTrackerEnabled(enabled) {
-        if (this._caretTrackingEnabled == enabled)
-            return;
-
-        this._caretTrackingEnabled = enabled;
-
-        if (enabled) {
-            this._focusCaretTracker.registerFocusListener();
-            this._focusCaretTracker.registerCaretListener();
-        } else {
-            this._focusCaretTracker.deregisterFocusListener();
-            this._focusCaretTracker.deregisterCaretListener();
-        }
-    },
-
-    _updateCaretPosition(accessible) {
-        if (this._updateCaretPositionId)
-            GLib.source_remove(this._updateCaretPositionId);
-        if (!this._keyboardRequested)
-            return;
-        this._updateCaretPositionId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
-            this._updateCaretPositionId = 0;
-
-            let currentWindow = global.display.focus_window;
-            if (!currentWindow) {
-                this.setCursorLocation(null);
-                return GLib.SOURCE_REMOVE;
-            }
-
-            let windowRect = currentWindow.get_frame_rect();
-            let text = accessible.get_text_iface();
-            let component = accessible.get_component_iface();
-
-            try {
-                let caretOffset = text.get_caret_offset();
-                let caretRect = text.get_character_extents(caretOffset, Atspi.CoordType.WINDOW);
-                let focusRect = component.get_extents(Atspi.CoordType.WINDOW);
-
-                if (caretRect.width == 0 && caretRect.height == 0)
-                    caretRect = focusRect;
-
-                this.setCursorLocation(currentWindow, caretRect.x, caretRect.y, caretRect.width, caretRect.height);
-            } catch (e) {
-                log('Error updating caret position for OSK: ' + e.message);
-            }
-
-            return GLib.SOURCE_REMOVE;
-        });
-
-        GLib.Source.set_name_by_id(this._updateCaretPositionId, '[gnome-shell] this._updateCaretPosition');
-    },
-
-    _focusIsTextEntry(accessible) {
-        try {
-            let role = accessible.get_role();
-            let stateSet = accessible.get_state_set();
-            return stateSet.contains(Atspi.StateType.EDITABLE) || role == Atspi.Role.TERMINAL;
-        } catch (e) {
-            log('Error determining accessible role: ' + e.message);
-            return false;
-        }
-    },
-
-    _onFocusChanged(caretTracker, event) {
-        let accessible = event.source;
-        if (!this._focusIsTextEntry(accessible))
-            return;
-
-        let focused = event.detail1 != 0;
-        if (focused) {
-            this._currentAccessible = accessible;
-            this._updateCaretPosition(accessible);
-            this.show(Main.layoutManager.focusIndex);
-        } else if (this._currentAccessible == accessible) {
-            this._currentAccessible = null;
-            this.hide();
-        }
-    },
-
-    _onCaretMoved(caretTracker, event) {
-        let accessible = event.source;
-        if (this._currentAccessible == accessible)
-            this._updateCaretPosition(accessible);
+    _onFocusPositionChanged(focusTracker) {
+        let rect = focusTracker.getCurrentRect();
+        this.setCursorLocation(focusTracker.currentWindow, rect.x, rect.y, rect.width, rect.height);
     },
 
     _lastDeviceIsTouchscreen() {
@@ -650,8 +632,6 @@ var Keyboard = new Lang.Class({
         if (!this._enabled && !this._keyboardController)
             return;
 
-        this._setCaretTrackerEnabled(this._enabled);
-
         if (this._enabled && !this._keyboardController)
             this._setupKeyboard();
         else if (!this._enabled)
@@ -1029,11 +1009,14 @@ var Keyboard = new Lang.Class({
         if (!this._keyboardRequested)
             return;
 
-        if (this._currentAccessible)
-            this._updateCaretPosition(this._currentAccessible);
         Main.layoutManager.keyboardIndex = monitor;
         this._relayout();
         Main.layoutManager.showKeyboard();
+
+        if (this._delayedAnimFocusWindow) {
+            this._setAnimationWindow(this._delayedAnimFocusWindow);
+            this._delayedAnimFocusWindow = null;
+        }
     },
 
     hide() {
@@ -1104,8 +1087,9 @@ var Keyboard = new Lang.Class({
         window.move_frame(true, frameRect.x, frameRect.y);
     },
 
-    _animateWindow(window, show, deltaY) {
+    _animateWindow(window, show) {
         let windowActor = window.get_compositor_private();
+        let deltaY = Main.layoutManager.keyboardBox.height;
         if (!windowActor)
             return;
 
@@ -1126,35 +1110,39 @@ var Keyboard = new Lang.Class({
         }
     },
 
-    setCursorLocation(window, x, y , w, h) {
-        if (window == this._oskFocusWindow)
+    _setAnimationWindow(window) {
+        if (this._animFocusedWindow == window)
             return;
 
-        if (this._oskFocusWindow) {
-            let display = global.display;
+        if (this._animFocusedWindow)
+            this._animateWindow(this._animFocusedWindow, false);
+        if (window)
+            this._animateWindow(window, true);
 
-            if (display.get_grab_op() == Meta.GrabOp.NONE ||
-                display.get_focus_window() != this._oskFocusWindow)
-                this._animateWindow(this._oskFocusWindow, false, this._oskFocusWindowDelta);
+        this._animFocusedWindow = window;
+    },
 
-            this._oskFocusWindow = null;
-            this._oskFocusWindowDelta = null;
-        }
+    setCursorLocation(window, x, y , w, h) {
+        let monitor = Main.layoutManager.keyboardMonitor;
 
-        if (window) {
-            let monitor = Main.layoutManager.keyboardMonitor;
+        if (window && monitor) {
             let keyboardHeight = Main.layoutManager.keyboardBox.height;
-            let frameRect = window.get_frame_rect();
-            let windowActor = window.get_compositor_private();
-            let delta = 0;
+            let focusObscured = false;
 
-            if (monitor && frameRect.y + y + h >= monitor.height - keyboardHeight)
-                delta = keyboardHeight;
-
-            this._animateWindow(window, true, delta);
-            this._oskFocusWindow = window;
-            this._oskFocusWindowDelta = delta;
+            if (y + h >= monitor.y + monitor.height - keyboardHeight) {
+                if (this._keyboardVisible)
+                    this._setAnimationWindow(window);
+                else
+                    this._delayedAnimFocusWindow = window;
+            } else if (y < keyboardHeight) {
+                this._delayedAnimFocusWindow = null;
+                this._setAnimationWindow(null);
+            }
+        } else {
+            this._setAnimationWindow(null);
         }
+
+        this._oskFocusWindow = window;
     },
 });